package com.headsen.v2;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
import com.google.common.collect.ImmutableMap;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;

/**
 * 缓存版本2：在版本1的基础上细化锁的粒度
 * <p>
 * 1、分段锁-部分
 * 2、读不加锁
 * <p>
 * 1、淘汰的并发一致性
 * 2、二级结构未必快 二次hash, 复杂的扩容、复杂的锁
 *
 * @author zxs
 * @version 1.0
 * @date 2022/7/30 9:32
 */
public class MyCacheV2<K, V> implements Cache<K, V> {
    /**
     * 数据结构
     */
    private Map<K, Entry<K, V>>[] dataArr;
    private Entry<K, V>[] headArr;
    private Entry<K, V>[] tailArr;
    /**
     * 链表的长度 TODO
     */
    private volatile int[] headCount;

    /**
     * 配置数据。0不失效
     */
    private int maxSize;
    private int expireTimeAfterRefresh;
    private int expireTimeAfterAccess;
    private final int concurrencyLevel = 8;

    /**
     * @param initSize
     * @param maxSize
     * @param expireTimeAfterRefresh 毫秒
     * @param expireTimeAfterAccess
     */
    public MyCacheV2(int initSize, int maxSize, int expireTimeAfterRefresh, int expireTimeAfterAccess) {
        //参数校验
        this.maxSize = maxSize;
        this.expireTimeAfterRefresh = expireTimeAfterRefresh;
        this.expireTimeAfterAccess = expireTimeAfterAccess;
        //并发读concurrencyLevel
        this.dataArr = new HashMap[concurrencyLevel];
        this.headArr = new Entry[concurrencyLevel];
        this.tailArr = new Entry[concurrencyLevel];
        this.headCount = new int[concurrencyLevel];
        for (int i = 0; i < concurrencyLevel; i++) {
            dataArr[i] = new HashMap<>(initSize / concurrencyLevel + (initSize % concurrencyLevel > 0 ? 1 : 0));
            headArr[i] = new Entry<>();
            tailArr[i] = new Entry<>();
            headArr[i].next = tailArr[i];
            tailArr[i].prev = headArr[i];
        }

    }

    /**
     * @param key
     * @return
     */
    @Override
    public V getIfPresent(Object key) {
        if (key == null) {
            return null;
        }
        int index = getIndex(key);
        Map<K, Entry<K, V>> data = dataArr[index];
        Entry<K, V> entry = data.get(key);
        if (entry == null) {
            return null;
        }
        long l = System.currentTimeMillis();
        if (isExpire(l, entry)) {
            return null;
        }
        if (expireTimeAfterAccess != 0) {
            entry.expireTime = Math.max(System.currentTimeMillis() + expireTimeAfterAccess, entry.expireTime);
        }
        // 这里会加锁 当前节点移动到头部
        shiftFirst(index, entry);
        return entry.value;
    }

    @Override
    public V get(K key, Callable<? extends V> loader) throws ExecutionException {
        V v = getIfPresent(key);
        if (v != null) {
            return v;
        }
        try {
            v = loader.call();
        } catch (Exception e) {
            throw new ExecutionException(e);
        }
        if (v == null) {
            throw new NullPointerException();
        }
        put(key, v);
        return v;
    }

    @Override
    public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void put(K key, V value) {
        if (key == null || value == null) {
            throw new IllegalArgumentException("入参不合法");
        }
        int index = getIndex(key);
        Map<K, Entry<K, V>> data = dataArr[index];
        Entry<K, V> vEntry = null;
        synchronized (data) {
            vEntry = data.get(key);
            if (vEntry == null) {
                vEntry = new Entry<>();
                data.put(key, vEntry);
            }
            vEntry.key = key;
            vEntry.value = value;
            if (expireTimeAfterRefresh != 0) {
                vEntry.expireTime = Math.max(System.currentTimeMillis() + expireTimeAfterRefresh, vEntry.expireTime);
            }
            shiftFirst(index, vEntry);
            removeInvalid(index);
        }


    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        if (m == null) {
            throw new IllegalArgumentException("入参不合法");
        }
        m.entrySet().forEach(x -> put(x.getKey(), x.getValue()));
    }

    @Override
    public void invalidate(Object key) {
        if (key == null) {
            throw new IllegalArgumentException("入参不合法");
        }
        //这里直接删了
        int index = getIndex(key);
        Map<K, Entry<K, V>> data = dataArr[index];
        synchronized (data) {
            Entry<K, V> vEntry = data.get(key);
            if (vEntry != null) {
                data.remove(key);
                remove(index, vEntry);
            }
        }
    }

    @Override
    public void invalidateAll(Iterable<?> keys) {
        if (keys == null) {
            throw new IllegalArgumentException("入参不合法");
        }
        keys.forEach(x -> invalidate(x));
    }

    @Override
    public void invalidateAll() {
        for (int i = 0; i < dataArr.length; i++) {
            Map<K, Entry<K, V>> data = dataArr[i];
            synchronized (data) {
                data.clear();
                synchronized (headArr[i]) {
                    headArr[i].next = null;
                    headCount[i] = 0;
                }

            }
        }
    }

    @Override
    public long size() {
        removeInvalid(-1);
        //TODO 待优化，可见性问题
        return Arrays.stream(headCount).sum();
    }

    @Override
    public CacheStats stats() {
        throw new UnsupportedOperationException();
    }

    @Override
    public ConcurrentMap<K, V> asMap() {
        ConcurrentHashMap<K, V> concurrentHashMap = new ConcurrentHashMap<>();
        for (Map<K, Entry<K, V>> data : dataArr) {
            synchronized (data) {
                data.entrySet().forEach(x -> concurrentHashMap.put(x.getKey(), x.getValue().value));
            }
        }
        return concurrentHashMap;
    }

    @Override
    public void cleanUp() {
        invalidateAll();
    }

    /**
     * 节点移动到头部
     *
     * @param index
     * @param entry
     */
    private void shiftFirst(int index, Entry<K, V> entry) {
        Entry<K, V> head = headArr[index];
        synchronized (head) {
            if (head.next == entry) {
                return;
            }
            remove(index, entry);
            entry.prev = head;
            entry.next = head.next;
            head.next.prev = entry;
            head.next = entry;
            headCount[index]++;
        }
    }

    /**
     * 从链表移除节点
     *
     * @param index
     * @param entry
     */
    private void remove(int index, Entry<K, V> entry) {
        Entry<K, V> head = headArr[index];
        //新节点
        if (entry.next == null) {
            return;
        }
        synchronized (head) {
            headCount[index]--;
            entry.prev.next = entry.next;
            entry.next.prev = entry.prev;
        }
    }

    /**
     * 清理失效的数据
     */
    private void removeInvalid(int index) {
        long l = System.currentTimeMillis();
        if (index < 0) {
            for (int i = 0; i < dataArr.length; i++) {
                removeInvalid(i);
            }
            return;
        }

        Map<K, Entry<K, V>> data = dataArr[index];
        int splitNum = maxSize / concurrencyLevel + (maxSize % concurrencyLevel == 0 ? 0 : 1);
        synchronized (data) {
            Entry<K, V> vEntry = tailArr[index].prev;
            while (vEntry != null && vEntry != headArr[index] && (isExpire(l, vEntry) || headCount[index] > splitNum)) {
                remove(index, vEntry);
                data.remove(vEntry.key);
                vEntry = vEntry.prev;
            }
        }
    }

    private boolean isExpire(long l, Entry<K, V> vEntry) {
        return (vEntry.expireTime != 0 && vEntry.expireTime < l);
    }


    private int getIndex(Object key) {
        //对比V1多了一次hash,默认hash性能不高
//        return key.hashCode() % concurrencyLevel;
        return key.hashCode() & (concurrencyLevel - 1);
    }

    /**
     * 缓存元素封装
     *
     * @param <V>
     */
    private final class Entry<K, V> {
        /**
         * 业务值
         */
        private K key;
        /**
         * 业务值
         */
        private V value;

        /**
         * 失效时间
         */
        private long expireTime;

        /**
         * 双向链表
         */
        private Entry<K, V> prev;
        private Entry<K, V> next;
    }
}
